from abc import ABCMeta, abstractmethod
from sqlalchemy.orm import Session
from docker import DockerClient
from app.controller.model.storage import GetNodeStatusItem, GetNodeStatusRes
from app.dao.storage import get_storage, get_storage_count
from app.docker import Client
from app.controller.DockerSwarmCtl import GetNodeList


class NodeStatuser(metaclass=ABCMeta):
    @abstractmethod
    def status(self) -> GetNodeStatusRes:
        pass


class NodeStatuserImpl(NodeStatuser):
    db: Session
    page: int
    size: int
    client: DockerClient
    Nodenum: int
    Nodelist: list
    dic: dict
    countlist: list

    def __init__(self, db: Session, client: DockerClient) -> None:
        super().__init__()
        self.db = db
        self.client = client
        self.Nodelist = GetNodeList()
        self.countlist = []
        self.dic = {}

    def _get_service_form_db(self):
        return get_storage(self.db)

    def _get_service_num_form_db(self):
        return get_storage_count(self.db)

    def _get_node_addr(self, main_node_id):
        ins = self.client.api.inspect_node(main_node_id)
        return ins['Status']['Addr']

    def _set_res_by_Nodelist(self):
        self.Nodenum = len(self.Nodelist)
        a = 0
        for node in self.Nodelist:
            self.countlist.append(0)
            addr = node.Addr
            self.dic[addr] = a
            a += 1

    def _get_hostname_by_addr(self, addr: str):
        for node in self.Nodelist:
            if node.Addr == addr:
                return node.Hostname
        return None

    def _get_detail_by_service_id(self, id):
        services = self.client.services.list(filters={'id': id})
        if len(services) == 0:
            return None
        service = services[0]
        tasks = service.tasks()

        main_node_addr = ''

        for task in tasks:
            if task['Status']['State'] == 'running':
                main_node_id = task['NodeID']
                main_node_addr = self._get_node_addr(main_node_id)
                tar_index = self.dic[main_node_addr]
                self.countlist[tar_index] += 1

    def _get_datanode_detail_by_namenodeservice_id(self, id):
        service = self.client.services.get(id)
        namenode_name = service.name
        length = len(namenode_name)
        services = self.client.services.list()
        for serv in services:
            serv_id = serv.id
            sv = self.client.services.get(serv_id)
            if sv.name != namenode_name and sv.name[0:length] == namenode_name:
                self._get_detail_by_service_id(sv.id)

    def status(self) -> GetNodeStatusRes:
        self._set_res_by_Nodelist()
        storages = self._get_service_form_db()
        count = self._get_service_num_form_db()

        for storage in storages:
            self._get_detail_by_service_id(storage.service_id)
            if storage.type == 1:
                self._get_datanode_detail_by_namenodeservice_id(
                    storage.service_id)

        res = []
        for node in self.Nodelist:
            tarcount = self.countlist[self.dic[node.Addr]]
            res.append(GetNodeStatusItem(
                item=node.Hostname, ip=node.Addr, count=tarcount))
        return GetNodeStatusRes(data=res)


def get_node_statuser(db: Session) -> NodeStatuser:
    return NodeStatuserImpl(db, client=Client().client)
